package afox.mvn.plugin.jy;

/*
 * Copyright 2011-2013 Alexander Fox <alex7fox@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.settings.Settings;
import org.sonatype.aether.RepositorySystem;
import org.sonatype.aether.RepositorySystemSession;
import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.resolution.ArtifactRequest;
import org.sonatype.aether.resolution.ArtifactResolutionException;
import org.sonatype.aether.resolution.ArtifactResult;
import org.sonatype.aether.util.artifact.DefaultArtifact;

//import org.apache.maven.artifact.DefaultArtifact;

/**
 * Goal which executes a python file.
 * 
 * @requiresDependencyResolution
 * 
 * @goal execfile
 * 
 * @phase process-sources
 */
public class JythonMojo extends AbstractMojo {
    /**
     * Location of python file.
     * 
     * @parameter expression="${execfile.path}"
     */
    private File pythonFile;
    /**
     * Python script code
     * 
     * @parameter expression="${execfile.code}"
     */
    private String pythonCode;
    /**
     * Verbose mode.
     * 
     * @parameter expression="${execfile.verbose}" default-value=Boolean.FALSE
     */
    private Boolean verbose;
    /**
     * Validate System Path
     * 
     * @parameter expression="${execfile.validateSystemPath}"
     *            default-value=Boolean.TRUE
     */
    private Boolean validateSystemPath;
    /**
     * Jython path, see python.path
     * 
     * @parameter expression="${execfile.python.path}"
     */
    private String pythonPath;
    /**
     * Maven project
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    public MavenProject project;
    /**
     * Maven settings
     * 
     * @parameter expression="${settings}"
     * @required
     * @readonly
     */
    public Settings settings;
    /**
     * Options as Map
     * 
     * @parameter expression="${execfile.python.options}"
     */
    public Map options;

    /**
     * Dependencies
     * 
     * @parameter expression="${execfile.python.dependencies}"
     */
    public JyDependency[] dependencies;

    /**
     * Helper class to assist in attaching artifacts to the project instance.
     * project-helper instance, used to make addition of resources simpler.
     * 
     * @component
     * @required
     * @readonly
     */
    private MavenProjectHelper projectHelper;

    /**
     * 
     * The entry point to Aether, i.e. the component doing all the work.
     * 
     * @component
     */
    private RepositorySystem repoSystem;

    /**
     * 
     * The current repository/network configuration of Maven.
     * 
     * @parameter default-value="${repositorySystemSession}"
     * @readonly
     */
    private RepositorySystemSession repoSession;

    /**
     * 
     * The project's remote repositories to use for the resolution of plugins
     * and their dependencies.
     * 
     * @parameter default-value="${project.remotePluginRepositories}"
     * @readonly
     */
    private List<RemoteRepository> remoteRepos;

    /**
     * Default constructor
     */
    public JythonMojo() {
        super();
    }

    /**
     * Default execute mojo function
     */
    public void execute() throws MojoExecutionException {
        if (new Double(System.getProperty("java.class.version")) < 50.0) {
            throw new MojoExecutionException("Unsupported java class version");
        }
        if (pythonFile != null) {
            executeFile(pythonFile);
        }
        if (pythonCode != null) {
            executeObject(pythonCode);
        }
    }

    /**
     * Execute File based Script
     * 
     * @param Object
     *            Script Object Reader or String
     */
    private void executeFile(File f) throws MojoExecutionException {
        if (f.exists()) {
            if (verbose) {
                getLog().info("Executing: " + f.getPath());
            }
            FileInputStream ins = null;
            try {
                ins = new FileInputStream(f);
            } catch (FileNotFoundException e) {
                throw new MojoExecutionException("Error can not find file: " + f.getPath(), e);
            }
            executeObject(ins);
        } else {
            throw new MojoExecutionException("Error can not find file: " + f.getPath());
        }
    }

    /**
     * Execute Script object
     * 
     * @param Object
     *            Script Object Reader or String
     */
    private void executeObject(Object o) throws MojoExecutionException {
        List<String> sysPath = new ArrayList<String>();
        if (pythonPath != null) {
            sysPath.addAll(Arrays.asList(pythonPath.split(":")));
        }
        for (JyDependency dependency : dependencies) {
            try {
                File artifact = download(dependency.getGroupId()+":"+dependency.getArtifactId()+":"+dependency.getVersion());
                sysPath.add(artifact.getAbsolutePath());
            } catch (ArtifactResolutionException e) {
                throw new MojoExecutionException("Cannot resolve artifact for: " + dependency, e);
            }
        }
        if (verbose) {
            getLog().info("Python system path: " + sysPath.toString());
        }
        if (validateSystemPath) {
            validateSysPath(sysPath);
        }
        JyScript j = new JyScript(sysPath.toArray(new String[sysPath.size()]));
        Object scriptReturnValue = null;
        Map<String, Object> bindings = new HashMap<String, Object>();
        bindings.put("__mvn_plugin__", this);
        scriptReturnValue = j.eval(o, bindings);
    }

    /**
     * Validate python sys path
     */
    private void validateSysPath(List<String> sysPath) {
        for (String path : sysPath) {
            File f = new File(path);
            if (!f.exists()) {
                getLog().warn("System path element not found: " + path);
            }
        }
    }
    
    /**
     * Download artifact and return path
     * @param coords
     * @return File path to artifact
     * @throws ArtifactResolutionException
     */
    public File download(String coords) throws ArtifactResolutionException {
        ArtifactRequest request = new ArtifactRequest();
        request.setArtifact(new DefaultArtifact(coords));
        request.setRepositories( remoteRepos );
        ArtifactResult result = repoSystem.resolveArtifact(repoSession, request);
        Artifact artifact = result.getArtifact();
        return artifact.getFile();
    }
}
